/**
 * 功能说明:
 * 功能作者:
 * 创建日期:
 * 版权归属:每特教育|蚂蚁课堂所有 www.itmayiedu.com
 */
package com.bigbigrain.jiagou.redis;

import cn.hutool.core.util.IdUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

// 如何生成token
@Component
public class RedisToken {

    @Autowired
	private RedisUtils redisUtils;

	private static final long TOKENTIMEOUT = 60 * 60;

	public String getToken() {
		// 生成token 规则保证 临时且唯一 不支持分布式场景 分布式全局ID生成规则
		String token = "token:" + IdUtil.createSnowflake(1, 1).nextId();
		// 如何保证token临时 （缓存）使用redis 实现缓存
        redisUtils.set(token, token, TOKENTIMEOUT);
		return token;
	}

	// 1.在调用接口之前生成对应的令牌(Token), 存放在Redis
	// 2.调用接口的时候，将该令牌放入的请求头中
	// 3.接口获取对应的令牌,如果能够获取该令牌(将当前令牌删除掉) 就直接执行该访问的业务逻辑
	// 4.接口获取对应的令牌,如果获取不到该令牌 直接返回请勿重复提交
	//todo:目前加锁这个方式在不集群的情况下可以，集群后会有问题 因为synchronized是jvm级别的锁 后期改造优化
	public synchronized boolean findToken(String tokenKey) {
		// 3.接口获取对应的令牌,如果能够获取该(从redis获取令牌)令牌(将当前令牌删除掉) 就直接执行该访问的业务逻辑
		/*这是以前的写法
		String tokenValue = redisUtils.get(tokenKey).toString();
		if (StrUtil.isEmpty(tokenValue)) {
			return false;
		}
		// 保证每个接口对应的token 只能访问一次，保证接口幂等性问题
		redisUtils.delete(tokenValue);
		return true;
		*/
		//张胖子分析，高并发的场景下 会同时进入 A进入到删除方法未执行时，另外一个线程进入到查询，这样也会执行一次
		//所以改造成删除，删除成功就是存在，删除失败就是不存在
		//redis是单线程的 上面的synchronized可以不加
		return redisUtils.delete(tokenKey);
	}
}
